import type { target } from "./index.d";
import { hasChange, isObject } from "../shared/index";
import { isTracking, trackEffects, triggerEffects } from "./effect";
import { reactive } from "./reactive";

/**ref的类 */  //因为ref使用场景大多数是简单数据类型，string number这种，我们要想知道是否get或者set，之前的proxy就做不到了，需要使用一个类。
export class RefImpl<T = any>{
    /**这个ref的值。可能是reactive数据或者普通简单数据类型 */
    private _value: T;
    /**这个ref的原始值，没有经过任何处理，是一个普通的数据 */
    private _rawValue: T;
    /**用于判断是否是ref的一个标识 */
    public __v_isRef: boolean = true
    /**这个ref对应的依赖Set */
    public dep: Set<any>
    constructor(value: T) {
        this._value = convert(value)//如果value是个对象，那么需要把这个对象转为reactive，再放到ref.value中
        this._rawValue = value
        this.dep = new Set()
    }
    /**该ref的value值 */
    get value() { //当读取ref.value时，会触发get
        trackRefValue(this)
        return this._value
    }
    set value(newValue: T) {
        if (hasChange(newValue, this._rawValue)) { //做对比的时候，需要用原始值去对比，避免value是个reactive时对比出错
            //一定要记得先修改value，再去触发依赖，避免错误
            this._rawValue = newValue
            this._value = convert(newValue)//set的时候也要转换
            triggerEffects(this.dep)
        }
    }
}
/**把当前的依赖添加到ref的dep中  */
function trackRefValue(ref: RefImpl) {
    if (isTracking()) {
        trackEffects(ref.dep)
    }
}
/**根据是否是对象，返回原数据或者reactive后的数据
 * @param value 原数据
 * @returns 根据是否是对象，返回原数据或者reactive后的数据
 */
function convert(value: any) {
    return isObject(value) ? reactive(value) : value
}

/**ref函数 
 * @param value 值
 */
export function ref<T>(value: T) {
    return new RefImpl(value)
}
/**是否是ref */
export function isRef(ref: any): boolean {
    return !!ref?.__v_isRef //如果有__v_isRef这个标识，说明就是ref。有可能是undefined，所以需要用 !! 转布尔值
}
/**解构ref。 如果是ref，就返回ref.value，如果不是，直接返回原值 */
export function unRef(ref: any) {
    return isRef(ref) ? ref.value : ref
}
/** 使用proxy代理数据，使得ref不需要.value。 
 * - 当对象中某个属性是ref时，调用此函数，会返回一个proxy，去读取该属性时，就不需要 .value 了。
 * - 使用场景：template中，我们使用setup中的ref不需要.value
 * - 示例可以看 src\reactivity\tests\ref.spec.ts 中的 proxyRefs
 * @param objectWithRefs 原始对象
 */
export function proxyRefs(objectWithRefs: target) {
    return new Proxy(objectWithRefs, {
        get(target, key) {
            return unRef(Reflect.get(target, key))
        },
        set(target, key, value) {
            //在set的时候要注意，如果修改后的value是ref类型，那么实际上就是 target.key = value (替换掉这个ref)
            //如果传递过来的value是普通数据类型，那么就是 target.key.value = value （在原ref的基础上修改）

            if (isRef(target[key]) && !isRef(value)) { //如果target[key]是ref类型，且设置的value不是ref类型
                return (target[key].value = value)
            } else {//其它情况都是直接修改原值
                return Reflect.set(target, key, value)
            }
        }
    });
}